//
// Created by Tony on 2024/8/2.
//

#include "INVOKE_VIRTUAL.h"
#include "../rt_code/Class.h"
#include "../rt_code/ClassTable.h"
#include <memory>
#include <stdexcept>

namespace Runtime {

using std::make_shared;
using std::runtime_error;

// 当前实现与INVOKE_SPECIAL一样，是因为还未支持类的继承和方法的override
void INVOKE_VIRTUAL::execute(RtData::Frame& frame) {
    auto& curMethod = frame.getMethod();
    shared_ptr<RtCode::Class> curClass = curMethod->getOwnerClass();
    auto& curConstPool = curClass->getConstantPool();
    auto* methodRef = curConstPool.getMethodRef(mIndex);
    auto method = methodRef->resolvedMethod();

    auto* obj = frame.getRefFromTop(0);
    if (obj == nullptr) {
        // TODO
        throw runtime_error("tlang.NullPointerException");
    }

    if (method->isNative()) {
        auto newFrame = RtData::Frame(nullptr, method);
        newFrame.setSlot(0, frame.popSlot()); // pass arg this

        auto argSlotCount = method->getArgSlotCount();
        for (int i = 0; i < argSlotCount - 1; i++) {
            auto slot = frame.popSlot();
            newFrame.setSlot(argSlotCount - 1 - i, slot);
        }

        shared_ptr<RtCode::ClassTable> classTable = curClass->getClassTable();
        auto nativeMethod = classTable->getNativeMethodTable()->get(
            method->getOwnerClass()->getName(), method->getName());
        nativeMethod(newFrame);
        if (!method->isReturnVoid()) {
            auto slot = newFrame.popSlot();
            frame.pushSlot(slot);
        }
        return;
    }

    auto stack = frame.getStack();
    auto newFrame = make_shared<RtData::Frame>(stack, method);
    stack->push(newFrame);
    newFrame->setSlot(0, frame.popSlot()); // pass arg this

    auto argSlotCount = method->getArgSlotCount();
    for (int i = 0; i < argSlotCount - 1; i++) {
        auto slot = frame.popSlot();
        newFrame->setSlot(argSlotCount - 1 - i, slot);
    }
}

} // namespace Runtime
